package com.polaris.live.ui.live.widget

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.LayoutTransition
import android.animation.ObjectAnimator
import android.content.Context
import android.net.Uri
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.blankj.utilcode.util.ScreenUtils
import com.blankj.utilcode.util.SpanUtils
import com.blankj.utilcode.util.SpanUtils.ALIGN_CENTER
import com.polaris.live.common.ext.setOnSingleClickListener
import com.polaris.live.common.manager.CoroutineScopeManager
import com.polaris.live.common.util.AppCodeUtils
import com.polaris.live.common.util.JsonUtils
import com.polaris.live.databinding.WidgetLiveFloatViewBinding
import com.polaris.live.im.sfs.bean.live.LiveFloatMessagePayloadBean
import com.polaris.live.im.sfs.bean.live.SendLiveFloatingMessage
import com.polaris.live.manager.DownFileManager
import com.polaris.live.utils.CommonUtils
import com.polaris.live.utils.file.FileUtils
import com.polaris.live.utils.image.ImageUtils.loadNetImageBitmap
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.libpag.PAGFile
import java.io.File
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean

/**
 * LiveFloatView
 *
 * @author Created by 半仙 on 2023/11/28/028 15:45
 */
class LiveFloatView(context: Context, attributeSet: AttributeSet?) :
    FrameLayout(context, attributeSet) {

    private val mWaitingList = ConcurrentLinkedQueue<SendLiveFloatingMessage>()

    private var mCurrentView: View? = null

    private val mAnimating = AtomicBoolean(false)

    private var mAnimJob: Job? = null
    private var mBindJob: Job? = null

    init {
        layoutTransition = LayoutTransition().apply {
            setAnimateParentHierarchy(false)
        }
    }

    private var mClickListener: (bean: SendLiveFloatingMessage) -> Unit = {}

    fun setOnSingleClickListener(chick: (bean: SendLiveFloatingMessage) -> Unit) {
        mClickListener = chick
    }

    fun addFloatView(bean: SendLiveFloatingMessage) {
        if (mAnimating.get()) {
            mWaitingList.add(bean)
        } else {
            downShowView(bean)
        }
    }

    private fun downShowView(bean: SendLiveFloatingMessage) {
        downLoadPag(bean) {
            it?.let {
                showCome(bean)
            }
        }

    }

    private fun showCome(bean: SendLiveFloatingMessage) {
        mAnimating.set(true)
        addAndRunAnim(bean) {
            showAnim(this.root)
        }
    }

    private fun showAnim(view: View) {
        view.post {
            // 计算视图的起始位置
            val initialX =
                ScreenUtils.getAppScreenWidth() + view.width // 假设起始位置为屏幕右侧之外
            // 设置视图的初始位置
            view.x = initialX.toFloat()
            // 创建平移动画
            val finalX =
                (ScreenUtils.getAppScreenWidth() - view.width) / 2f // 计算视图在屏幕中间的最终位置
            val animator: ObjectAnimator =
                ObjectAnimator.ofFloat(view, "translationX", finalX)
            animator.interpolator = AccelerateInterpolator()
            // 设置动画时长
            animator.duration = COME_OUT

            // 创建平移动画对象
            val animator2 = ObjectAnimator.ofFloat(view, "translationX", finalX, -view.width.toFloat())
            // 设置动画时长
            animator2.duration = COME_OUT
            animator2.interpolator = AccelerateInterpolator()
            animator2.startDelay = WAIT

            // 将两组动画结合起来
            val mCombinedAnimatorSet = AnimatorSet()
            mCombinedAnimatorSet.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    view.visibility = GONE
                    this@LiveFloatView.post {
                        this@LiveFloatView.removeAllViews()
                        mAnimating.set(false)
                        mWaitingList.poll()?.let { addFloatView(it) }
                    }
                }
            })
            mCombinedAnimatorSet.playSequentially(animator, animator2)
            mCombinedAnimatorSet.start()
        }
    }

    private fun addAndRunAnim(
        bean: SendLiveFloatingMessage,
        block: WidgetLiveFloatViewBinding.() -> Unit,
    ) {
        WidgetLiveFloatViewBinding.inflate(LayoutInflater.from(context), this, true).apply {
            mCurrentView = root
            this.chickLayout.setOnSingleClickListener {
                mClickListener.invoke(bean)
            }
            bingView(this, bean) {
                post {
                    block()
                }
            }
        }
    }

    private fun bingView(binding: WidgetLiveFloatViewBinding, bean: SendLiveFloatingMessage, ready: () -> Unit) {
        binding.apply {
            val rootLp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)
            root.layoutParams = rootLp

            mBindJob?.cancel()

            mBindJob = CoroutineScopeManager.ioScope.launch {
                try {
                    val payload = bean.payload?.let { JsonUtils.fromJson<LiveFloatMessagePayloadBean>(it) }

                    val span = SpanUtils.with(floatText)
                    val splitMessage = bean.content.split(SPLIT)
                    splitMessage.forEach {
                        if (it.contains(IMAGEAND)) {
                            val modifiedStr = it.replace(IMAGEAND, "")
                            val icon = loadNetImageBitmap(root.context, modifiedStr, 14, 14)
                            icon?.let { it1 -> span.appendImage(it1, ALIGN_CENTER) }
                        } else {
                            span.append(it)
                        }
                    }
                    val uri = Uri.parse(bean.pic)
                    val left = uri.getQueryParameter("left")?.toInt() ?: 0
                    val top = uri.getQueryParameter("top")?.toInt() ?: 0
                    val right = uri.getQueryParameter("right")?.toInt() ?: 0
                    val bottom = uri.getQueryParameter("bottom")?.toInt() ?: 0

                    val path = FileUtils.getFilePath(FileUtils.getPagFile(), bean.pic)

                    val pagFile = PAGFile.Load(path)

                    val width = pagFile.width()
                    val height = pagFile.height()

                    val screenWidth = ScreenUtils.getAppScreenWidth()
                    val ratioWidth = screenWidth / width.toFloat()
                    val screenHeight = (height * ratioWidth).toInt()

                    val ratioLeft = (left * ratioWidth).toInt()
                    val ratioTop = (top * ratioWidth).toInt()
                    val ratioRight = (right * ratioWidth).toInt()
                    val ratioBottom = (bottom * ratioWidth).toInt()

                    CommonUtils.pagReplace(context, pagFile, payload?.layers)

                    withContext(CoroutineScopeManager.mainDispatcher) {
                        rootLp.width = screenWidth
                        rootLp.height = screenHeight
                        // 计算视图的起始位置
                        val initialX =
                            ScreenUtils.getAppScreenWidth() + screenWidth
                        root.x = initialX.toFloat()
                        root.layoutParams = rootLp

                        val lp = floatPag.layoutParams as ConstraintLayout.LayoutParams
                        lp.width = screenWidth
                        lp.height = screenHeight
                        floatPag.layoutParams = lp

                        val textLp = floatText.layoutParams as ConstraintLayout.LayoutParams
                        textLp.topMargin = ratioTop
                        if (AppCodeUtils.isAR()) {
                            val constraintSet = ConstraintSet()
                            constraintSet.clone(binding.root)
                            floatText.gravity = Gravity.END or Gravity.CENTER_VERTICAL
                            textLp.rightMargin = ratioLeft
                            constraintSet.connect(
                                floatText.id,
                                ConstraintSet.END,
                                ConstraintSet.PARENT_ID,
                                ConstraintSet.END
                            )
                            constraintSet.applyTo(binding.root)
                        } else {
                            floatText.gravity = Gravity.START or Gravity.CENTER_VERTICAL
                            textLp.leftMargin = ratioLeft
                        }

                        textLp.width = screenWidth - ratioLeft - ratioRight
                        textLp.height = screenHeight - ratioTop - ratioBottom
                        floatText.layoutParams = textLp

                        val chickLp = chickLayout.layoutParams as ConstraintLayout.LayoutParams
                        chickLp.topMargin = ratioTop
                        chickLp.leftMargin = ratioLeft

                        chickLp.width = screenWidth - ratioLeft - ratioRight
                        chickLp.height = screenHeight - ratioTop - ratioBottom
                        chickLayout.layoutParams = chickLp

                        floatPag.apply {
                            setRepeatCount(-1)
                            composition = pagFile
                            play()
                        }
                        floatText.text = span.create()

                        ready.invoke()
                    }
                } finally {
                    if (coroutineContext[Job] == mBindJob) {
                        mBindJob = null
                    }
                }
            }
        }
    }

    private var mIsDownloading = false   //是否正在下载中
    private val mLoadQueue = ConcurrentLinkedQueue<SendLiveFloatingMessage>() //下载队列
    private var mPagJob: Job? = null

    //下载
    private fun downLoadPag(bean: SendLiveFloatingMessage, loadSuccess: (localPath: String?) -> Unit) {
        val filePath = FileUtils.getFilePath(FileUtils.getPagFile(), bean.pic)
        if (File(filePath).exists()) {
            loadSuccess.invoke(filePath)
            return
        }
        if (mIsDownloading) {
            mLoadQueue.add(bean)
            mWaitingList.add(bean)
            loadSuccess.invoke(null)
            return
        }

        mPagJob?.cancel()
        mPagJob = CoroutineScopeManager.ioScope.launch {
            try {
                mIsDownloading = true
                val loadFileCallback =
                    DownFileManager.loadFileCallback(FileUtils.getPagFile(), bean.pic)
                withContext(CoroutineScopeManager.mainDispatcher) {
                    mIsDownloading = false
                    loadFileCallback?.let {
                        loadSuccess.invoke(FileUtils.getFilePath(it.first, it.second))
                    }
                    mLoadQueue.poll()?.let {
                        downLoadPag(it) {

                        }
                    }
                }
            } finally {
                if (coroutineContext[Job] == mPagJob) {
                    mPagJob = null
                }
            }
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()

        mWaitingList.clear()
        mLoadQueue.clear()
        mAnimJob?.cancel()
        mAnimJob = null
        mBindJob?.cancel()
        mBindJob = null
        mPagJob?.cancel()
        mPagJob = null
    }

    companion object {

        const val COME_OUT = 500L
        const val WAIT = 4000L

        const val SPLIT = "<#>"
        const val IMAGEAND = "image="

        const val GIFT = 2L                 //礼物飘屏
        const val GAME = 3L                 //游戏飘屏
        const val ROOM = 1L                 //开房通知
        const val GENERAL = 4L              //通用
    }

}